package com.xh.hongaicodegenerator.utils;

import com.xh.hongaicodegenerator.model.enums.CodeGenTypeEnum;
import lombok.extern.slf4j.Slf4j;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;

import java.io.*;
import java.nio.file.Path;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 代码校验与安全补全工具
 * - 支持 Vue / HTML / JS / CSS / JSON 文件
 * - 校验尖括号 < >、括号 { } [ ] ( )
 * - 补全 HTML 与 Vue <template> 内容
 * - 忽略字符串、模板字符串、注释及箭头函数 =>
 */
@Slf4j
public class CodeValidAndFixTool {

    /** 支持的文件类型 */
    private static final List<String> CODE_EXTENSIONS = Arrays.asList(
            ".html", ".css", ".js", ".json", ".vue"
    );

    /** 排除特定文件 */
    private static final List<String> EXCLUDED_FILES = Arrays.asList(
            "package-lock.json", "yarn.lock", "pnpm-lock.yaml"
    );

    /** 排除特定目录 */
    private static final List<String> EXCLUDED_DIRS = Arrays.asList(
            "node_modules", "dist", "target", ".git"
    );

    /** 括号匹配规则 */
    private static final Map<Character, Character> BRACKET_PAIRS = Map.of(
            '{', '}',
            '[', ']',
            '(', ')'
    );

    private static final String[] VOID_ELEMENTS = {
            "area", "base", "br", "col", "embed", "hr",
            "img", "input", "link", "meta", "source", "track", "wbr"
    };

    // ===================== 公共入口 =====================
    public static List<String> validateDirectory(String directoryPath, CodeGenTypeEnum typeEnum) {
        File directory = new File(directoryPath);
        if (!directory.exists() || !directory.isDirectory()) {
            log.warn("未找到可检查的代码文件");
            return List.of("未找到可检查的代码文件");
        }

        List<String> errors = new ArrayList<>();
        validateFilesRecursively(directory, directory.toPath(), errors, typeEnum);
        return errors;
    }

    // ===================== 递归文件校验 =====================
    private static void validateFilesRecursively(File directory, Path basePath, List<String> errors, CodeGenTypeEnum typeEnum) {
        if (EXCLUDED_DIRS.contains(directory.getName())) return;

        File[] files = directory.listFiles();
        if (files == null) return;

        for (File file : files) {
            if (file.isDirectory()) {
                validateFilesRecursively(file, basePath, errors, typeEnum);
            } else {
                String fileName = file.getName();
                if (EXCLUDED_FILES.contains(fileName)) continue;
                boolean allowed = CODE_EXTENSIONS.stream().anyMatch(fileName::endsWith);
                if (!allowed) continue;
                validateFile(file, basePath, errors,typeEnum);
            }
        }
    }

    private static void validateFile(File file, Path basePath, List<String> errors, CodeGenTypeEnum typeEnum) {
        Path relativePath = basePath.relativize(file.toPath());
        String fileName = file.getName();
        try {
            String content = readFileContent(file);

            // 检查括号 { } [ ] ( )
            if (Arrays.asList(".vue", ".js", ".css", ".html").stream().anyMatch(fileName::endsWith)) {
                List<String> bracketErrors = new ArrayList<>();
                validateBrackets(relativePath.toString(), content, bracketErrors);
                if (!bracketErrors.isEmpty()) {
                    errors.addAll(bracketErrors);
                    return;
                }
            }

            // 补全 HTML
            if (fileName.endsWith(".html")&&!typeEnum.equals(CodeGenTypeEnum.VUE_PROJECT)) {
                String updatedContent = completeHtmlContentSafe(content, errors);
                writeFileContent(file, updatedContent);
            }
//            else if (fileName.endsWith(".vue")) {
////                String updatedContent = completeVueTemplateContentSafe(file.getPath(), content, errors);
////                writeFileContent(file, updatedContent);
//            }

        } catch (IOException e) {
            errors.add(relativePath + ": 无法读取文件, 错误: " + e.getMessage());
        }
    }

    private static String readFileContent(File file) throws IOException {
        StringBuilder contentBuilder = new StringBuilder();
        try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
            String line;
            while ((line = reader.readLine()) != null) contentBuilder.append(line).append("\n");
        }
        return contentBuilder.toString();
    }

    private static void writeFileContent(File file, String content) throws IOException {
        try (BufferedWriter writer = new BufferedWriter(new FileWriter(file))) {
            writer.write(content);
        }
    }

    // ===================== 括号校验 =====================
    private static void validateBrackets(String relativePath, String content, List<String> errors) {
        Stack<CharacterBracketInfo> stack = new Stack<>();
        boolean[] states = new boolean[5]; // singleQuote, doubleQuote, template, lineComment, blockComment
        String[] lines = content.split("\n");

        for (int lineNo = 0; lineNo < lines.length; lineNo++) {
            char[] chars = lines[lineNo].toCharArray();
            for (int i = 0; i < chars.length; i++) {
                char c = chars[i];
                char next = (i + 1 < chars.length) ? chars[i + 1] : 0;

                updateStringCommentState(c, next, states);

                if (states[0] || states[1] || states[2] || states[3] || states[4]) continue;

                if (BRACKET_PAIRS.containsKey(c)) {
                    stack.push(new CharacterBracketInfo(c, relativePath, lineNo + 1, i + 1));
                } else if (BRACKET_PAIRS.containsValue(c)) {
                    if (stack.isEmpty()) {
                        errors.add(relativePath + "中 行:" + (lineNo + 1) + ", 列:" + (i + 1) + " 存在错误: 没有匹配的开括号: " + c);
                        return;
                    } else {
                        CharacterBracketInfo last = stack.pop();
                        char expected = BRACKET_PAIRS.get(last.getBracket());
                        if (c != expected) {
                            errors.add(relativePath + "中 行:" + (lineNo + 1) + ", 列:" + (i + 1) +
                                    " 存在错误: 括号不匹配，找到: " + c + "，期望: " + expected);
                            return;
                        }
                    }
                }
            }
            states[3] = false; // 行注释关闭
        }

        while (!stack.isEmpty()) {
            CharacterBracketInfo unclosed = stack.pop();
            errors.add(relativePath + "中 行:" + unclosed.getLineNumber() + ", 列:" + unclosed.getColumn() +
                    " 存在错误: 未闭合的括号: " + unclosed.getBracket());
        }
    }

    private static void updateStringCommentState(char c, char next, boolean[] states) {
        boolean inSingleQuote = states[0], inDoubleQuote = states[1], inTemplate = states[2],
                inLineComment = states[3], inBlockComment = states[4];

        if (c == '\'' && !inDoubleQuote && !inTemplate && !inLineComment && !inBlockComment)
            states[0] = !inSingleQuote;
        else if (c == '"' && !inSingleQuote && !inTemplate && !inLineComment && !inBlockComment)
            states[1] = !inDoubleQuote;
        else if (c == '`' && !inSingleQuote && !inDoubleQuote && !inLineComment && !inBlockComment)
            states[2] = !inTemplate;
        else if (c == '/' && next == '/' && !inSingleQuote && !inDoubleQuote && !inTemplate && !inBlockComment)
            states[3] = true;
        else if (c == '/' && next == '*' && !inSingleQuote && !inDoubleQuote && !inTemplate && !inLineComment)
            states[4] = true;
        else if (c == '*' && next == '/' && inBlockComment)
            states[4] = false;
    }

    // ===================== Vue 尖括号与标签校验 =====================
    private static void validateVueAngleBrackets(String relativePath, String content, List<String> errors) {
        Stack<CharacterBracketInfo> angleStack = new Stack<>();
        Stack<TagInfo> tagStack = new Stack<>();
        boolean[] states = new boolean[5]; // singleQuote, doubleQuote, template, lineComment, blockComment

        String[] lines = content.split("\n");
        for (int lineNo = 0; lineNo < lines.length; lineNo++) {
            char[] chars = lines[lineNo].toCharArray();
            for (int i = 0; i < chars.length; i++) {
                char c = chars[i];
                char next = (i + 1 < chars.length) ? chars[i + 1] : 0;
                char prev = (i - 1 >= 0) ? chars[i - 1] : 0;

                updateStringCommentState(c, next, states);
                if (states[0] || states[1] || states[2] || states[3] || states[4]) continue;

                // 跳过箭头函数和比较运算符
                if ((c == '=' && next == '>') || (c == '<' && next == '=') || (c == '>' && next == '=')) {
                    i++;
                    continue;
                }

                // 检查尖括号
                if (c == '<') {
                    if (Character.isWhitespace(prev) && Character.isWhitespace(next)) continue;
                    angleStack.push(new CharacterBracketInfo(c, relativePath, lineNo + 1, i + 1));

                    int tagEnd = lines[lineNo].indexOf('>', i);
                    if (tagEnd != -1) {
                        String tagContent = lines[lineNo].substring(i + 1, tagEnd).trim().toLowerCase();
                        handleVueTag(tagContent, tagStack, relativePath, lineNo + 1, i + 1, errors);
                    }

                } else if (c == '>') {
                    if (Character.isWhitespace(prev) && Character.isWhitespace(next)) continue;
                    if (angleStack.isEmpty()) {
                        errors.add(relativePath + "中 行:" + (lineNo + 1) + ", 列:" + (i + 1) + " 存在错误: 没有匹配的 '<'");
                    } else {
                        CharacterBracketInfo last = angleStack.pop();
                        if (last.getBracket() != '<') {
                            errors.add(relativePath + "中 行:" + (lineNo + 1) + ", 列:" + (i + 1) + " 存在错误: 尖括号不匹配");
                        }
                    }
                }
            }
            states[3] = false; // 行注释关闭
        }

        while (!angleStack.isEmpty()) {
            CharacterBracketInfo unclosed = angleStack.pop();
            errors.add(relativePath + "中 行:" + unclosed.getLineNumber() + ", 列:" + unclosed.getColumn() +
                    " 存在错误: 未闭合的 '<'");
        }
        while (!tagStack.isEmpty()) {
            TagInfo unclosedTag = tagStack.pop();
            errors.add(relativePath + "中 行:" + unclosedTag.line + ", 列:" + unclosedTag.column +
                    " 存在错误: 未闭合的标签 <" + unclosedTag.tag + ">");
        }
    }

    private static void handleVueTag(String tagContent, Stack<TagInfo> tagStack, String relativePath,
                                     int line, int column, List<String> errors) {
        if (tagContent.startsWith("script") || tagContent.startsWith("style") || tagContent.startsWith("template")) {
            tagStack.push(new TagInfo(tagContent.split("\\s+")[0], line, column));
        } else if (tagContent.startsWith("/script") || tagContent.startsWith("/style") || tagContent.startsWith("/template")) {
            if (tagStack.isEmpty()) {
                errors.add(relativePath + "中 行:" + line + ", 列:" + column +
                        " 存在错误: 没有匹配的开始标签 " + tagContent);
            } else {
                TagInfo lastTag = tagStack.pop();
                String expected = lastTag.tag;
                String actual = tagContent.substring(1); // 去掉 '/'
                if (!expected.equals(actual)) {
                    errors.add(relativePath + "中 行:" + line + ", 列:" + column +
                            " 存在错误: 标签不匹配，找到: " + actual + "，期望: " + expected);
                }
            }
        }
    }

    private static class TagInfo {
        String tag;
        int line;
        int column;

        TagInfo(String tag, int line, int column) {
            this.tag = tag;
            this.line = line;
            this.column = column;
        }
    }

    private static class CharacterBracketInfo {
        private final char bracket;
        private final String filePath;
        private final int lineNumber;
        private final int column;

        public CharacterBracketInfo(char bracket, String filePath, int lineNumber, int column) {
            this.bracket = bracket;
            this.filePath = filePath;
            this.lineNumber = lineNumber;
            this.column = column;
        }

        public char getBracket() { return bracket; }
        public int getLineNumber() { return lineNumber; }
        public int getColumn() { return column; }
    }

    // ===================== HTML / Vue 补全 =====================
    private static String completeVueTemplateContentSafe(String filePath, String content, List<String> errors) {
        try {
            // 查找最外层 <template> 范围
            int startTagOpen = content.indexOf("<template");
            int startTagClose = (startTagOpen != -1) ? content.indexOf('>', startTagOpen) : -1;
            int endTagOpen = (startTagClose != -1) ? content.lastIndexOf("</template") : -1;
            int endTagClose = (endTagOpen != -1) ? content.indexOf('>', endTagOpen) : -1;

            if (startTagOpen == -1 || startTagClose == -1 || endTagOpen == -1 || endTagClose == -1) {
                errors.add(filePath + ": 错误 - <template> 和 </template> 标签不完整或缺失");
                return content;
            }

            String beforeTemplate = content.substring(0, startTagClose + 1);
            String templateInner = content.substring(startTagClose + 1, endTagOpen);
            String afterTemplate = content.substring(endTagOpen);

            // 调用内部递归补全逻辑
            String completedInner = completeVueTemplateContentSafeInner(templateInner, errors, filePath, "  ");

            return beforeTemplate + "\n" + completedInner + "\n" + afterTemplate;
        } catch (Exception ex) {
            errors.add(filePath + ": <template> 补全异常: " + ex.getMessage());
            return content;
        }
    }

    private static String completeVueTemplateContentSafeInner(String templateContent, List<String> errors, String filePath, String baseIndent) {
        try {
            // =================== 递归保护内层 <template> ===================
            Map<String, String> innerTemplateMap = new LinkedHashMap<>();
            Pattern innerTplPattern = Pattern.compile("(?m)^(\\s*)<template\\b[^>]*>[\\s\\S]*?</template>");
            Matcher tplMatcher = innerTplPattern.matcher(templateContent);
            int idxTpl = 0;
            StringBuffer sbTpl = new StringBuffer();
            while (tplMatcher.find()) {
                String fullInnerTpl = tplMatcher.group();
                String innerIndent = tplMatcher.group(1); // 行首缩进

                // 拆分开闭标签，保留属性
                int openTagEnd = fullInnerTpl.indexOf(">") + 1;
                String innerTplOpenTag = fullInnerTpl.substring(0, openTagEnd); // 包含属性
                String innerTplBody = fullInnerTpl.substring(openTagEnd, fullInnerTpl.lastIndexOf("</template"));
                String innerTplCloseTag = "</template>";

                // 递归补全内部内容，缩进增加两个空格
                String completedInner = completeVueTemplateContentSafeInner(innerTplBody, errors, filePath, innerIndent + "  ");

                // 组合完整占位符
                String fullCompletedTpl = innerTplOpenTag + "\n" + completedInner + "\n" + innerIndent + innerTplCloseTag;
                String placeholder = "__INNER_TEMPLATE_" + idxTpl + "__";
                innerTemplateMap.put(placeholder, fullCompletedTpl);
                tplMatcher.appendReplacement(sbTpl, placeholder);
                idxTpl++;
            }
            tplMatcher.appendTail(sbTpl);
            String protectedTemplate = sbTpl.toString();

            // =================== 保护 PascalCase 标签 (Vue 组件) ===================
            Map<String, String> compMap = new LinkedHashMap<>();
            Pattern compPattern = Pattern.compile("</?[A-Z][A-Za-z0-9\\-]*\\b[^>]*>");
            Matcher matcher = compPattern.matcher(protectedTemplate);
            StringBuffer sbComp = new StringBuffer();
            int idxComp = 0;
            while (matcher.find()) {
                String fullTag = matcher.group();
                String placeholder = "__COMP_" + idxComp + "__";
                compMap.put(placeholder, fullTag);
                matcher.appendReplacement(sbComp, placeholder);
                idxComp++;
            }
            matcher.appendTail(sbComp);
            String toParse = sbComp.toString();

            // =================== 保护 HTML void 元素 (img, br, input 等) ===================
            Map<String, String> voidMap = new LinkedHashMap<>();
            for (String tag : VOID_ELEMENTS) {
                Pattern voidPattern = Pattern.compile("<" + tag + "\\b[^>]*/?>", Pattern.CASE_INSENSITIVE);
                Matcher vm = voidPattern.matcher(toParse);
                int idxVoid = 0;
                StringBuffer sbVoid = new StringBuffer();
                while (vm.find()) {
                    String fullVoidTag = vm.group();
                    String placeholder = "__VOID_" + tag.toUpperCase() + "_" + idxVoid + "__";
                    voidMap.put(placeholder, fullVoidTag);
                    vm.appendReplacement(sbVoid, placeholder);
                    idxVoid++;
                }
                vm.appendTail(sbVoid);
                toParse = sbVoid.toString();
            }

            // =================== Jsoup XML 解析补全内部缺失标签 ===================
            Document doc = Jsoup.parse(toParse, "", org.jsoup.parser.Parser.xmlParser());
            doc.outputSettings().prettyPrint(true).indentAmount(2).outline(false);
            String completed = doc.outerHtml();

            // 去掉 Jsoup 自动生成的 <html><head> 标签
            completed = completed.replaceFirst("(?s)<html>.*?<body>", "").replaceFirst("(?s)</body>.*?</html>", "");

            // =================== 恢复 PascalCase 标签 ===================
            for (Map.Entry<String, String> e : compMap.entrySet()) {
                completed = completed.replace(e.getKey(), e.getValue());
            }

            // =================== 恢复 void 元素 ===================
            for (Map.Entry<String, String> e : voidMap.entrySet()) {
                completed = completed.replace(e.getKey(), e.getValue());
            }

            // =================== 恢复内层 template ===================
            for (Map.Entry<String, String> e : innerTemplateMap.entrySet()) {
                completed = completed.replace(e.getKey(), e.getValue());
            }

            // =================== 恢复转义字符 ===================
            completed = restoreEscapedCharacters(completed);

            // =================== 添加基准缩进 ===================
            String[] lines = completed.split("\n");
            StringBuilder sb = new StringBuilder();
            for (String line : lines) {
                if (line.trim().isEmpty()) {
                    sb.append("\n");
                } else {
                    sb.append(baseIndent).append(line).append("\n");
                }
            }

            return sb.toString().replaceAll("\\s+\\n", "\n"); // 去掉多余空格换行
        } catch (Exception ex) {
            errors.add(filePath + ": 内层 <template> 补全异常: " + ex.getMessage());
            return templateContent;
        }
    }




    private static String completeHtmlContentSafe(String content, List<String> errors) {
        try {
            // =================== 保护 PascalCase 标签 (Vue/JSX 组件) ===================
            Map<String, String> placeholderMap = new LinkedHashMap<>();
            Pattern compPattern = Pattern.compile("<(/?)([A-Z][A-Za-z0-9\\-]*)\\b");
            Matcher matcher = compPattern.matcher(content);
            StringBuilder sb = new StringBuilder();
            int last = 0;
            while (matcher.find()) {
                sb.append(content, last, matcher.start());
                String slash = matcher.group(1);
                String tag = matcher.group(2);
                String placeholder = "__COMP_" + placeholderMap.size() + "__";
                placeholderMap.put(placeholder, tag);
                sb.append("<").append(slash).append(placeholder);
                last = matcher.end(2);
            }
            sb.append(content.substring(last));
            String protectedContent = sb.toString();

            // =================== 保护 HTML void 元素 ===================
            Map<String, String> voidMap = new LinkedHashMap<>();
            for (String tag : VOID_ELEMENTS) {
                Pattern voidPattern = Pattern.compile("<" + tag + "\\b[^>]*/?>", Pattern.CASE_INSENSITIVE);
                Matcher vm = voidPattern.matcher(protectedContent);
                int idxVoid = 0;
                StringBuffer sbVoid = new StringBuffer();
                while (vm.find()) {
                    String fullVoidTag = vm.group();
                    String placeholder = "__VOID_" + tag.toUpperCase() + "_" + idxVoid + "__";
                    voidMap.put(placeholder, fullVoidTag);
                    vm.appendReplacement(sbVoid, placeholder);
                    idxVoid++;
                }
                vm.appendTail(sbVoid);
                protectedContent = sbVoid.toString();
            }

            // =================== Jsoup 解析 ===================
            Document doc = Jsoup.parse(protectedContent);
            String completed = doc.outerHtml();

            // 恢复 PascalCase 标签
            for (Map.Entry<String, String> e : placeholderMap.entrySet()) {
                completed = completed.replace(e.getKey(), e.getValue());
            }

            // 恢复 void 元素
            for (Map.Entry<String, String> e : voidMap.entrySet()) {
                completed = completed.replace(e.getKey(), e.getValue());
            }

            return restoreEscapedCharacters(completed);

        } catch (Exception ex) {
            errors.add("HTML 补全异常: " + ex.getMessage());
            return content;
        }
    }


    private static String restoreEscapedCharacters(String html) {
        if (html == null || html.isEmpty()) return html;
        return html.replace("&lt;", "<")
                .replace("&gt;", ">")
                .replace("&amp;", "&")
                .replace("&quot;", "\"")
                .replace("&#39;", "'");
    }
}